package com.my.test.thread.conditition.waitandnotify;

// 阻塞和等待区别 阻塞会自动唤醒，等待需要另外一个线程主动唤醒


//Object.wait(),Object.notify(),Object.notifyAll()都是Object的方法，换句话说，就是每个类里面都有这些方法。


//Object.wait()：释放当前对象锁，并进入阻塞队列
//Object.notify()：唤醒当前对象阻塞队列里的任一线程（并不保证唤醒哪一个）
//Object.notifyAll()：唤醒当前对象阻塞队列里的所有线程

//为什么这三个方法要与synchronized一起使用呢？解释这个问题之前，我们先要了解几个知识点
//每一个对象都有一个与之对应的监视器
//每一个监视器里面都有一个该对象的锁和一个等待队列和一个同步队列


//wait()方法的语义有两个，一是释放当前对象锁，另一个是进入阻塞队列，可以看到，这些操作都是与监视器相关的，当然要指定一个监视器才能完成这个操作了

//notify()方法也是一样的，用来唤醒一个线程，你要去唤醒，首先你得知道他在哪儿，所以必须先找到该对象，也就是获取该对象的锁，当获取到该对象的锁之后，才能去该对象的对应的等待队列去唤醒一个线程。值得注意的是，只有当执行唤醒工作的线程离开同步块，即释放锁之后，被唤醒线程才能去竞争锁。

//notifyAll()方法和notify()一样，只不过是唤醒等待队列中的所有线程

//因wait()而导致阻塞的线程是放在阻塞队列中的，因竞争失败导致的阻塞是放在同步队列中的，notify()/notifyAll()实质上是把阻塞队列中的线程放到同步队列中去

//为了便于理解，你可以把线程想象成一个个列车，对象想象成车站，每一个车站每一次能跑一班车，这样理解起来就比较容易了。

//值得提的一点是，synchronized是一个非公平的锁，如果竞争激烈的话，可能导致某些线程一直得不到执行。

//全文地址请点击：https://blog.csdn.net/qq_39907763/article/details/79301813?utm_source=copy


import java.util.concurrent.TimeUnit;
public class WaitNotifyCase {
    public static void main(String[] args) {
        final Object lock = new Object();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread B is waiting to get lock");
                synchronized (lock) {
                    System.out.println("thread B get lock");
                    try {
                        // 即使执行notify方法也不会立即唤醒另外一个线程
                        lock.notify();
                        System.out.println("thread B do notify method");
                        TimeUnit.SECONDS.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread A is waiting to get lock");
                synchronized (lock) {
                    try {
                        System.out.println("thread A get lock");
                        TimeUnit.SECONDS.sleep(1);
                        System.out.println("thread A do wait method");
                        // 执行wait方法必须持有当前对象的monitor,所以放在同步代码块里执行
                        lock.wait();
                        System.out.println("wait end");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();


    }
}